class_name VoxelTerrain

extends Node3D

@export var chunk_prefab: PackedScene

var _orgin: TerrainChunk = null

var noise: FastNoiseLite

func _enter_tree() -> void:
	_orgin = chunk_prefab.instantiate()
	_orgin.pos = Vector3i.ZERO
	noise = FastNoiseLite.new()
	noise.seed = Time.get_ticks_usec()
	noise.noise_type = FastNoiseLite.TYPE_PERLIN
	for x in 16:
		for z in 16:
			var n = noise.get_noise_2d(x, z)
			var h = floori(remap(n, -1, 1, 0, 1) * 16.0)
			for y in h:
				var color = Color.WEB_GRAY
				if y == 0:
					color = Color.BLACK
				elif y == h - 1:
					color = Color.WEB_GREEN
				elif h - y < 4:
					color = Color.SADDLE_BROWN
				
				_orgin.set_voxel(Vector3i(x, y, z), color)
			
	add_child(_orgin)


func _ready() -> void:
	set_voxel(Vector3.ZERO, Color.BLACK)


func find_chunk(chunk_pos: Vector3i, out_neighbors: Array = []) -> TerrainChunk:
	if out_neighbors.size() >= 6:
		var ret = null;
		for i in get_child_count():
			var child: TerrainChunk = get_child(i)
			if child.pos == chunk_pos:
				ret = child
			else:
				var neighbor = ChunkInstance.get_neighbor_idx(chunk_pos, child.pos)
				if neighbor != null:
					out_neighbors[neighbor as int] = child
		return ret
	else:
		for i in get_child_count():
			var child: TerrainChunk = get_child(i)
			if child.pos == chunk_pos:
				return child
		
	return null


func get_chunk(chunk_pos: Vector3i) -> TerrainChunk:
	var neighbors = []
	neighbors.resize(6)
	var chunk := find_chunk(chunk_pos, neighbors)
	if chunk == null:
		chunk = chunk_prefab.instantiate()
		chunk.pos = chunk_pos
		if chunk_pos.y == 0:
			var begin := chunk_pos * 16
			for x in 16:
				for z in 16:
					var n = noise.get_noise_2d(begin.x + x, begin.z + z)
					var h = floori(remap(n, -1, 1, 0, 1) * 16.0)
					for y in h:
						var color = Color.WEB_GRAY
						if y == 0:
							color = Color.BLACK
						elif y == h - 1:
							color = Color.WEB_GREEN
						elif h - y < 4:
							color = Color.SADDLE_BROWN
				
						chunk.set_voxel(Vector3i(x, y, z), color)
			
		for i in neighbors.size():
			if neighbors[i] != null:
				chunk.set_neighbor(i, neighbors[i])
		
		add_child(chunk)
	
	return chunk


func set_voxel(pos: Vector3i, color: Color) -> void:
	var chunk_pos := Vector3i((Vector3(pos) / 16.0).floor())
	var chunk := get_chunk(chunk_pos)
	
	chunk.set_voxel(pos - chunk_pos * 16, color)


func remove_voxel(pos: Vector3i) -> void:
	var chunk_pos := Vector3i((Vector3(pos) / 16.0).floor())
	var chunk := find_chunk(chunk_pos)
	if chunk == null:
		return
	
	chunk.remove_voxel(pos - chunk_pos * 16)


func get_voxel(pos: Vector3i) -> Variant:
	var chunk_pos := Vector3i((Vector3(pos) / 16.0).floor())
	var chunk := find_chunk(chunk_pos)
	if chunk == null:
		return null
	
	return chunk.get_voxel(pos - chunk_pos * 16)
